home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / actionrp / nhplusx.bin / nhplusx / nhplusX / X11 / winX.c~ < prev   
Text File  |  1995-09-11  |  49KB  |  1,781 lines

  1. /*    SCCS Id: @(#)winX.c    3.1    93/02/17          */
  2. /* Copyright (c) Dean Luick, 1992                  */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /*
  6.  * "Main" file for the X window-port.  This contains most of the interface
  7.  * routines.  Please see doc/window.doc for an description of the window
  8.  * interface.
  9.  */
  10.  
  11. #ifndef SYSV
  12. #define PRESERVE_NO_SYSV    /* X11 include files may define SYSV */
  13. #endif
  14.  
  15. #ifdef MSDOS            /* from compiler */
  16. #define SHORT_FILENAMES
  17. #endif
  18.  
  19. #include <X11/Intrinsic.h>
  20. #include <X11/StringDefs.h>
  21. #include <X11/Shell.h>
  22. #include <X11/Xaw/AsciiText.h>
  23. #include <X11/Xaw/Label.h>
  24. #include <X11/Xaw/Form.h>
  25. #include <X11/Xaw/Cardinals.h>
  26. #include <X11/Xatom.h>
  27. #include <X11/Xos.h>
  28.  
  29. /* for color support; should be ifdef TEXTCOLOR, but must come before hack.h */
  30. #ifdef SHORT_FILENAMES
  31. #include <X11/IntrinsP.h>
  32. #else
  33. #include <X11/IntrinsicP.h>
  34. #endif
  35.  
  36. #ifdef PRESERVE_NO_SYSV
  37. # ifdef SYSV
  38. #  undef SYSV
  39. # endif
  40. # undef PRESERVE_NO_SYSV
  41. #endif
  42.  
  43. #include "hack.h"
  44. #include "winX.h"
  45.  
  46. /* Should be defined in <X11/Intrinsic.h> but you never know */
  47. #ifndef XtSpecificationRelease
  48. #define XtSpecificationRelease 0
  49. #endif
  50.  
  51. /*
  52.  * Icons.
  53.  */
  54. #include "../win/X11/nh72icon"
  55. #include "../win/X11/nh56icon"
  56. #include "../win/X11/nh32icon"
  57.  
  58. static struct icon_info {
  59.     const char *name;
  60.     char *bits;
  61.     unsigned width, height;
  62. } icon_data[] = {
  63.     { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
  64.     { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
  65.     { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
  66.     { NULL, NULL, 0, 0 }
  67. };
  68.  
  69. /*
  70.  * Private global variables (shared among the window port files).
  71.  */
  72. struct xwindow window_list[MAX_WINDOWS];
  73. AppResources appResources;
  74. void (*input_func)();
  75. int click_x, click_y, click_button;    /* Click position on a map window   */
  76.                     /* (filled by set_button_values()). */
  77.  
  78.  
  79. /* Interface definition, for windows.c */
  80. struct window_procs X11_procs = {
  81.     "X11",
  82.     X11_init_nhwindows,
  83.     X11_player_selection,
  84.     X11_askname,
  85.     X11_get_nh_event,
  86.     X11_exit_nhwindows,
  87.     X11_suspend_nhwindows,
  88.     X11_resume_nhwindows,
  89.     X11_create_nhwindow,
  90.     X11_clear_nhwindow,
  91.     X11_display_nhwindow,
  92.     X11_destroy_nhwindow,
  93.     X11_curs,
  94.     X11_putstr,
  95.     X11_display_file,
  96.     X11_start_menu,
  97.     X11_add_menu,
  98.     X11_end_menu,
  99.     X11_select_menu,
  100.     X11_update_inventory,
  101.     X11_mark_synch,
  102.     X11_wait_synch,
  103. #ifdef CLIPPING
  104.     X11_cliparound,
  105. #endif
  106.     X11_print_glyph,
  107.     X11_raw_print,
  108.     X11_raw_print_bold,
  109.     X11_nhgetch,
  110.     X11_nh_poskey,
  111.     X11_nhbell,
  112.     X11_doprev_message,
  113.     X11_yn_function,
  114.     X11_getlin,
  115. #ifdef COM_COMPL
  116.     X11_get_ext_cmd,
  117. #endif /* COM_COMPL */
  118.     X11_number_pad,
  119.     X11_delay_output,
  120. #ifdef CHANGE_COLOR    /* only a Mac option currently */
  121.     donull,
  122.     donull,
  123. #endif
  124.     /* other defs that really should go away (they're tty specific) */
  125.     X11_start_screen,
  126.     X11_end_screen,
  127.     genl_outrip,
  128. };
  129.  
  130. /*
  131.  * Local functions.
  132.  */
  133. static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*));
  134. static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*));
  135. static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*));
  136. static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*));
  137. static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*));
  138. static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*));
  139. static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*));
  140. static int FDECL(input_event, (int));
  141. static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *));
  142. static void NDECL(init_standard_windows);
  143.  
  144.  
  145. /*
  146.  * Local variables.
  147.  */
  148. static boolean x_inited = FALSE;    /* TRUE if window system is set up. */
  149. static winid message_win = WIN_ERR,    /* These are the winids of the        */
  150.          map_win     = WIN_ERR,    /*   message, map, and status        */
  151.          status_win  = WIN_ERR;    /*   windows, when they are created */
  152.                     /*   in init_windows().            */
  153. static Pixmap icon_pixmap = None;    /* Pixmap for icon.            */
  154.  
  155. /*
  156.  * Find the window structure that corresponds to the given widget.  Note
  157.  * that this is not the popup widget, nor the viewport, but the child.
  158.  */
  159. struct xwindow *
  160. find_widget(w)
  161.     Widget w;
  162. {
  163.     int windex;
  164.     struct xwindow *wp;
  165.  
  166.     /* This is sad.  Search to find the corresponding window. */
  167.     for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
  168.     if (wp->type != NHW_NONE &&
  169.         (wp->w == w || (wp->w && XtParent(wp->w) == w))) break;
  170.     if (windex == MAX_WINDOWS) panic("find_widget:  can't match widget");
  171.     return wp;
  172. }
  173.  
  174. /*
  175.  * Find a free window slot for use.
  176.  */
  177. static winid
  178. find_free_window()
  179. {
  180.     int windex;
  181.     struct xwindow *wp;
  182.  
  183.     for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++)
  184.     if (wp->type == NHW_NONE) break;
  185.  
  186.     if (windex == MAX_WINDOWS)
  187.     panic("find_free_window: no free windows!");
  188.     return (winid) windex;
  189. }
  190.  
  191. #ifdef TEXTCOLOR
  192. /*
  193.  * Color conversion.  The default X11 color converters don't try very
  194.  * hard to find matching colors in PseudoColor visuals.  If they can't
  195.  * allocate the exact color, they puke and give you something stupid.
  196.  * This is an attempt to find some close readonly cell and use it.
  197.  */
  198. XtConvertArgRec const nhcolorConvertArgs[] = {
  199.     {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen),
  200.      sizeof(Screen *)},
  201.     {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap),
  202.      sizeof(Colormap)}
  203. };
  204.  
  205. #define done(type, value) \
  206.     {                            \
  207.         if (toVal->addr != NULL) {                \
  208.         if (toVal->size < sizeof(type)) {        \
  209.             toVal->size = sizeof(type);            \
  210.             return False;                \
  211.         }                        \
  212.         *(type*)(toVal->addr) = (value);        \
  213.         }                            \
  214.         else {                        \
  215.         static type static_val;                \
  216.         static_val = (value);                \
  217.         toVal->addr = (genericptr_t)&static_val;    \
  218.         }                            \
  219.         toVal->size = sizeof(type);                \
  220.         return True;                    \
  221.     }
  222.  
  223. /* decl.h declares these, but it screws up structure references -dlc */
  224. #undef red
  225. #undef green
  226. #undef blue
  227.  
  228. /* Return True if something close was found. */
  229. Boolean
  230. nhCloseColor(screen, colormap, str, color)
  231. Screen     *screen;    /* screen to use */
  232. Colormap colormap;    /* the colormap to use */
  233. char     *str;        /* color name */
  234. XColor   *color;    /* the X color structure; changed only if successful */
  235. {
  236.     int        ncells;
  237.     long    cdiff = 16777216; /* 2^24; hopefully our map is smaller */
  238.     XColor    tmp;
  239.     static    XColor *table = 0;
  240.     register    i, j;
  241.     register long tdiff;
  242.  
  243.     /* if the screen doesn't have a big colormap, don't waste our time */
  244.     /* or if it's huge, and _some_ match should have been possible */
  245.     if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
  246.     return False;
  247.  
  248.     if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
  249.     return False;
  250.  
  251.     if (!table) {
  252.     table = (XColor *) XtCalloc(ncells, sizeof(XColor));
  253.     for(i=0; i<ncells; i++)
  254.         table[i].pixel = i;
  255.     XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
  256.     }
  257.  
  258.     /* go thru cells and look for the one with smallest diff */
  259.     /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
  260.     /* a more knowledgeable color person might improve this -dlc */
  261.     for(i=0; i<ncells; i++) {
  262.     if(table[i].flags == tmp.flags) {
  263.         j = (int)table[i].red - (int)tmp.red;
  264.         if(j < 0) j = -j;
  265.         tdiff = j;
  266.         j = (int)table[i].green - (int)tmp.green;
  267.         if(j < 0) j = -j;
  268.         tdiff += j;
  269.         j = (int)table[i].blue - (int)tmp.blue;
  270.         if(j < 0) j = -j;
  271.         tdiff += j;
  272.         if(tdiff < cdiff) {
  273.         cdiff = tdiff;
  274.         tmp.pixel = i; /* table[i].pixel == i */
  275.         }
  276.     }
  277.     }
  278.  
  279.     if(cdiff == 16777216) return False;    /* nothing found?! */
  280.  
  281.     /*
  282.      * Found something.  Return it and mark this color as used to avoid
  283.      * reuse.  Reuse causes major contrast problems :-)
  284.      */
  285.     color->pixel = tmp.pixel;
  286.     table[tmp.pixel].flags = 0;
  287.     return True;
  288. }
  289.  
  290. Boolean
  291. nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
  292. Display*    dpy;
  293. XrmValuePtr    args;
  294. Cardinal    *num_args;
  295. XrmValuePtr    fromVal;
  296. XrmValuePtr    toVal;
  297. XtPointer    *closure_ret;
  298. {
  299.     String        str = (String)fromVal->addr;
  300.     XColor        screenColor;
  301.     XColor        exactColor;
  302.     Screen        *screen;
  303.     XtAppContext    app = XtDisplayToApplicationContext(dpy);
  304.     Colormap        colormap;
  305.     Status        status;
  306.     String          params[1];
  307.     Cardinal        num_params=1;
  308.  
  309.     if (*num_args != 2) {
  310.      XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel",
  311.     "XtToolkitError",
  312.     "String to pixel conversion needs screen and colormap arguments",
  313.     (String *)0, (Cardinal *)0);
  314.      return False;
  315.     }
  316.  
  317.     screen = *((Screen **) args[0].addr);
  318.     colormap = *((Colormap *) args[1].addr);
  319.  
  320.     /* If Xt colors, use the Xt routine and hope for the best */
  321. #if (XtSpecificationRelease >= 5)
  322.     if ((strcmpi(str, XtDefaultBackground) == 0) ||
  323.     (strcmpi(str, XtDefaultForeground) == 0)) {
  324.     return
  325.       XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret);
  326.     }
  327. #else
  328.     if (strcmpi(str, XtDefaultBackground) == 0) {
  329.     *closure_ret = (char*)False;
  330.     done(Pixel, WhitePixelOfScreen(screen));
  331.     }
  332.     if (strcmpi(str, XtDefaultForeground) == 0) {
  333.     *closure_ret = (char*)False;
  334.     done(Pixel, BlackPixelOfScreen(screen));
  335.     }
  336. #endif
  337.  
  338.     status = XAllocNamedColor(DisplayOfScreen(screen), colormap,
  339.                   (char*)str, &screenColor, &exactColor);
  340.     if (status == 0) {
  341.     String msg, type;
  342.     params[0] = str;
  343.     /* Server returns a specific error code but Xlib discards it.  Ugh */
  344.     if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str,
  345.              &exactColor, &screenColor)) {
  346.         /* try to find another color that will do */
  347.         if (nhCloseColor(screen, colormap, (char*) str, &screenColor)) {
  348.         *closure_ret = (char*)True;
  349.         done(Pixel, screenColor.pixel);
  350.         }
  351.         type = "noColormap";
  352.         msg = "Cannot allocate colormap entry for \"%s\"";
  353.     }
  354.     else {
  355.         type = "badValue";
  356.         msg = "Color name \"%s\" is not defined";
  357.     }
  358.  
  359.     XtAppWarningMsg(app, type, "cvtStringToPixel",
  360.             "XtToolkitError", msg, params, &num_params);
  361.     *closure_ret = False;
  362.     return False;
  363.     } else {
  364.     *closure_ret = (char*)True;
  365.     done(Pixel, screenColor.pixel);
  366.     }
  367. }
  368.  
  369. /* ARGSUSED */
  370. static void
  371. nhFreePixel(app, toVal, closure, args, num_args)
  372. XtAppContext    app;
  373. XrmValuePtr    toVal;
  374. XtPointer    closure;
  375. XrmValuePtr    args;
  376. Cardinal    *num_args;
  377. {
  378.     Screen        *screen;
  379.     Colormap        colormap;
  380.  
  381.     if (*num_args != 2) {
  382.      XtAppWarningMsg(app, "wrongParameters",
  383.              "freePixel", "XtToolkitError",
  384.              "Freeing a pixel requires screen and colormap arguments",
  385.              (String *)0, (Cardinal *)0);
  386.      return;
  387.     }
  388.  
  389.     screen = *((Screen **) args[0].addr);
  390.     colormap = *((Colormap *) args[1].addr);
  391.  
  392.     if (closure) {
  393.     XFreeColors( DisplayOfScreen(screen), colormap,
  394.              (unsigned long*)toVal->addr, 1, (unsigned long)0
  395.             );
  396.     }
  397. }
  398. #endif /* TEXTCOLOR */
  399.  
  400. /* Global Functions ======================================================== */
  401. void
  402. X11_raw_print(str)
  403.     const char *str;
  404. {
  405.     (void) puts(str);
  406. }
  407.  
  408. void
  409. X11_raw_print_bold(str)
  410.     const char *str;
  411. {
  412.     (void) puts(str);
  413. }
  414.  
  415. void
  416. X11_curs(window, x, y)
  417.     winid window;
  418.     int x, y;
  419. {
  420.     check_winid(window);
  421.  
  422.     if (x < 0 || x >= COLNO) {
  423.     impossible("curs:  bad x value [%d]", x);
  424.     x = 0;
  425.     }
  426.     if (y < 0 || y >= ROWNO) {
  427.     impossible("curs:  bad y value [%d]", y);
  428.     y = 0;
  429.     }
  430.  
  431.     window_list[window].cursx = x;
  432.     window_list[window].cursy = y;
  433. }
  434.  
  435. void
  436. X11_putstr(window, attr, str)
  437.     winid window;
  438.     int attr;
  439.     const char *str;
  440. {
  441.     winid new_win;
  442.     struct xwindow *wp;
  443.  
  444.     check_winid(window);
  445.     wp = &window_list[window];
  446.  
  447.     switch (wp->type) {
  448.     case NHW_MESSAGE:
  449.         Strcpy(toplines, str);    /* for Norep(). */
  450.         append_message(wp, str);
  451.         break;
  452.     case NHW_STATUS:
  453.         adjust_status(wp, str);
  454.         break;
  455.     case NHW_MAP:
  456.         impossible("putstr: called on map window \"%s\"", str);
  457.         break;
  458.     case NHW_MENU:
  459.         if (wp->menu_information->is_menu) {
  460.         impossible(
  461.             "putstr:  called on a menu window, \"%s\" discarded",
  462.             str);
  463.         break;
  464.         }
  465.         /*
  466.          * Change this menu window into a text window by creating a
  467.          * new text window, then copying it to this winid.
  468.          */
  469.         new_win = X11_create_nhwindow(NHW_TEXT);
  470.         X11_destroy_nhwindow(window);
  471.         *wp = window_list[new_win];
  472.         window_list[new_win].type = NHW_NONE;    /* allow re-use */
  473.         /* fall though to add text */
  474.     case NHW_TEXT:
  475.         add_to_text_window(wp, attr, str);
  476.         break;
  477.     default:
  478.         impossible("putstr: unknown window type [%d] \"%s\"",
  479.                                 wp->type, str);
  480.     }
  481. }
  482.  
  483. /* We do event processing as a callback, so this is a null routine. */
  484. void X11_get_nh_event() { return; }
  485.  
  486. int
  487. X11_nhgetch()
  488. {
  489.     return input_event(EXIT_ON_KEY_PRESS);
  490. }
  491.  
  492.  
  493. int
  494. X11_nh_poskey(x, y, mod)
  495.     int *x, *y, *mod;
  496. {
  497.     int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
  498.  
  499.     if (val == 0) {    /* user clicked on a map window */
  500.     *x   = click_x;
  501.     *y   = click_y;
  502.     *mod = click_button;
  503.     }
  504.     return val;
  505. }
  506.  
  507.  
  508. winid
  509. X11_create_nhwindow(type)
  510.     int type;
  511. {
  512.     winid window;
  513.     struct xwindow *wp;
  514.  
  515.     if (!x_inited)
  516.     panic("create_nhwindow:  windows not initialized");
  517.  
  518.     /*
  519.      * We have already created the standard message, map, and status
  520.      * windows in the window init routine.  The first window of that
  521.      * type to be created becomes the standard.
  522.      *
  523.      * A better way to do this would be to say that init_nhwindows()
  524.      * has already defined these three windows.
  525.      */
  526.     if (type == NHW_MAP && map_win != WIN_ERR) {
  527.     window = map_win;
  528.     map_win = WIN_ERR;
  529.     return window;
  530.     }
  531.     if (type == NHW_MESSAGE && message_win != WIN_ERR) {
  532.     window = message_win;
  533.     message_win = WIN_ERR;
  534.     return window;
  535.     }
  536.     if (type == NHW_STATUS && status_win != WIN_ERR) {
  537.     window = status_win;
  538.     status_win = WIN_ERR;
  539.     return window;
  540.     }
  541.  
  542.     window = find_free_window();
  543.     wp = &window_list[window];
  544.  
  545.     /* The create routines will set type, popup, w, and Win_info. */
  546.     wp->prevx = wp->prevy = wp->cursx = wp->cursy =
  547.                 wp->pixel_width = wp->pixel_height = 0;
  548.  
  549.     switch (type) {
  550.     case NHW_MAP:
  551.         create_map_window(wp, TRUE, (Widget) 0);
  552.         break;
  553.     case NHW_MESSAGE:
  554.         create_message_window(wp, TRUE, (Widget) 0);
  555.         break;
  556.     case NHW_STATUS:
  557.         create_status_window(wp, TRUE, (Widget) 0);
  558.         break;
  559.     case NHW_MENU:
  560.         create_menu_window(wp);
  561.         break;
  562.     case NHW_TEXT:
  563.         create_text_window(wp);
  564.         break;
  565.     default:
  566.         panic("create_nhwindow: unknown type [%d]\n", type);
  567.         break;
  568.     }
  569.     return window;
  570. }
  571.  
  572. void
  573. X11_clear_nhwindow(window)
  574.     winid window;
  575. {
  576.     struct xwindow *wp;
  577.  
  578.     check_winid(window);
  579.     wp = &window_list[window];
  580.  
  581.     switch (wp->type) {
  582.     case NHW_MAP:
  583.         clear_map_window(wp);
  584.         break;
  585.     case NHW_STATUS:
  586.     case NHW_TEXT:
  587.     case NHW_MENU:
  588.     case NHW_MESSAGE:
  589.         /* do nothing for these window types */
  590.         break;
  591.     default:
  592.         panic("clear_nhwindow: unknown window type [%d]\n", wp->type);
  593.         break;
  594.     }
  595. }
  596.  
  597. void
  598. X11_display_nhwindow(window, blocking)
  599.     winid window;
  600.     boolean blocking;
  601. {
  602.     struct xwindow *wp;
  603.     check_winid(window);
  604.  
  605.     wp = &window_list[window];
  606.  
  607.     switch (wp->type) {
  608.     case NHW_MAP:
  609.         if (wp->popup)
  610.         nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  611.         /*else
  612.          *  XtMapWidget(toplevel);
  613.          *
  614.          * We don't need to do the above because we never have
  615.          * MappedWhenManaged unset because the DEC server doesn't
  616.          * like it.  See comment above XtSetMappedWhenManaged() in
  617.          * init_standard_windows().
  618.          */
  619.         display_map_window(wp);    /* flush map */
  620.  
  621.         /*
  622.          * We need to flush the message window here due to the way the tty
  623.          * port is set up.  To flush a window, you need to call this
  624.          * routine.  However, the tty port _pauses_ with a --more-- if we
  625.          * do a display_nhwindow(WIN_MESSAGE, FALSE).  Thus, we can't call
  626.          * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
  627.          * get a --more-- after every line.
  628.          *
  629.          * Perhaps the window document should mention that when the map
  630.          * is flushed, everything on the three main windows should be
  631.          * flushed.  Note: we don't need to flush the status window
  632.          * because we don't buffer changes.
  633.          */
  634.         if (WIN_MESSAGE != WIN_ERR)
  635.         display_message_window(&window_list[WIN_MESSAGE]);
  636.         if (blocking)
  637.         (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
  638.         break;
  639.     case NHW_MESSAGE:
  640.         if (wp->popup)
  641.          nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  642.         /*else
  643.          *    XtMapWidget(toplevel);
  644.          *
  645.          * See comment in NHW_MAP case.
  646.          */
  647.  
  648.         display_message_window(wp);    /* flush messages */
  649.         break;
  650.     case NHW_STATUS:
  651.         if (wp->popup)
  652.         nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  653.         /*else
  654.          *    XtMapWidget(toplevel);
  655.          *
  656.          * See comment in NHW_MAP case.
  657.          */
  658.  
  659.         break;            /* no flushing necessary */
  660.     case NHW_MENU:
  661.         (void) X11_select_menu(window); /* pop up menu (global routine) */
  662.         break;
  663.     case NHW_TEXT:
  664.         display_text_window(wp, blocking);    /* pop up text window */
  665.         break;
  666.     default:
  667.         panic("display_nhwindow: unknown window type [%d]\n", wp->type);
  668.         break;
  669.     }
  670. }
  671.  
  672. void
  673. X11_destroy_nhwindow(window)
  674.     winid window;
  675. {
  676.     struct xwindow *wp;
  677.     check_winid(window);
  678.  
  679.     /*
  680.      * "Zap" known windows, but don't destroy them.  We need to keep the
  681.      * toplevel widget popped up so that later windows (e.g. tombstone)
  682.      * are visible on DECWindow systems.  This is due to the virtual
  683.      * roots that the DECWindow wm creates.
  684.      */
  685.     if (window == WIN_MESSAGE) {
  686.     WIN_MESSAGE = WIN_ERR;
  687.     flags.window_inited = 0;
  688.     return;
  689.     } else if (window == WIN_MAP) {
  690.     WIN_MAP = WIN_ERR;
  691.     return;
  692.     } else if (window == WIN_STATUS) {
  693.     WIN_STATUS = WIN_ERR;
  694.     return;
  695.     } else if (window == WIN_INVEN) {
  696.     WIN_INVEN = WIN_ERR;
  697.     return;
  698.     }
  699.  
  700.     wp = &window_list[window];
  701.  
  702.     switch (wp->type) {
  703.     case NHW_MAP:
  704.         destroy_map_window(wp);
  705.         break;
  706.     case NHW_MENU:
  707.         destroy_menu_window(wp);
  708.         break;
  709.     case NHW_TEXT:
  710.         destroy_text_window(wp);
  711.         break;
  712.     case NHW_STATUS:
  713.         destroy_status_window(wp);
  714.         break;
  715.     case NHW_MESSAGE:
  716.         destroy_message_window(wp);
  717.         break;
  718.     default:
  719.         panic("destroy_nhwindow: unknown window type [%d]", wp->type);
  720.         break;
  721.     }
  722. }
  723.  
  724. /* We don't implement a continous invent screen, so this is null. */
  725. void X11_update_inventory() { return; }
  726.  
  727. /* The current implementation has all of the saved lines on the screen. */
  728. int X11_doprev_message() { return 0; }
  729.  
  730. void
  731. X11_nhbell()
  732. {
  733.     /* We can't use XBell until toplevel has been initialized. */
  734.     if (x_inited)
  735.     XBell(XtDisplay(toplevel), 0);
  736.     /* else print ^G ?? */
  737. }
  738.  
  739. void X11_mark_synch()
  740. {
  741.     if (x_inited) {
  742.     /*
  743.      * the window document is a bit unclear about the status of text
  744.      * that has been pline()d but not displayed w/display_nhwindow(),
  745.      * though the main code and tty code assume that a pline() followed
  746.      * by mark_synch() results in the text being seen, even if
  747.      * display_nhwindow() wasn't called.  Duplicate this behavior.
  748.      */
  749.     if (WIN_MESSAGE != WIN_ERR)
  750.         display_message_window(&window_list[WIN_MESSAGE]);
  751.     XSync(XtDisplay(toplevel), False);
  752.     }
  753. }
  754.  
  755. void X11_wait_synch() {if (x_inited) XFlush(XtDisplay(toplevel)); }
  756.  
  757.  
  758. /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
  759. void X11_resume_nhwindows() { return; }
  760.  
  761. /* ARGSUSED */
  762. void X11_suspend_nhwindows(str) const char *str; { return; }
  763.  
  764. /* Under X, we don't need to initialize the number pad. */
  765. /* ARGSUSED */
  766. void X11_number_pad(state) int state; { return; } /* called from options.c */
  767.  
  768.  
  769. void X11_start_screen() { return; } /* called from setftty() in unixtty.c */
  770. void X11_end_screen() { return; }   /* called from settty() in unixtty.c */
  771.  
  772.  
  773. /* init and exit nhwindows ------------------------------------------------- */
  774.  
  775. XtAppContext app_context;        /* context of application */
  776. Widget         toplevel = (Widget) 0;    /* toplevel widget */
  777. Atom         wm_delete_window;        /* To pop-down windows */
  778.  
  779. static XtActionsRec actions[] = {
  780.     {"dismiss_file",    dismiss_file},    /* action for file viewing widget */
  781.     {"delete_file",    delete_file},    /* action for file delete-window */
  782.     {"dismiss_text",    dismiss_text},    /* button action for text widget */
  783.     {"delete_text",    delete_text},    /* delete action for text widget */
  784.     {"key_dismiss_text",key_dismiss_text},/* key action for text widget */
  785.     {"menu_key",    menu_key},    /* action for menu accelerators */
  786.     {"yn_key",        yn_key},    /* action for yn accelerators */
  787.     {"yn_delete",    yn_delete},    /* action for yn delete-window */
  788.     {"askname_delete",    askname_delete},/* action for askname delete-window */
  789.     {"getline_delete",    getline_delete},/* action for getline delete-window */
  790.     {"menu_delete",    menu_delete},    /* action for menu delete-window */
  791.     {"ec_key",        ec_key},    /* action for extended commands */
  792.     {"ec_delete",    ec_delete},    /* action for ext-com menu delete */
  793.     {"ps_key",        ps_key},    /* action for player selection */
  794.     {"X11_hangup",    X11_hangup},    /* action for delete of top-level */
  795. };
  796.  
  797. static XtResource resources[] = {
  798.     { "slow", "Slow", XtRBoolean, sizeof(Boolean),
  799. /*      XtOffset(AppResources *,slow), XtRString, "False" },*/
  800.       XtOffset(AppResources *,slow), XtRString, "False" },
  801.     { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean),
  802.       XtOffset(AppResources *,autofocus), XtRString, "False" },
  803.     { "message_line", "Message_line", XtRBoolean, sizeof(Boolean),
  804.       XtOffset(AppResources *,message_line), XtRString, "False" },
  805.     { "icon", "Icon", XtRString, sizeof(String),
  806.       XtOffset(AppResources *,icon), XtRString, "nh72" },
  807. };
  808.  
  809. void
  810. X11_init_nhwindows()
  811. {
  812.     static const char *banner_text[] = {
  813.     "NetHack",
  814.     "Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993",
  815.     "by Stichting Mathematisch Centrum and M. Stephenson.",
  816.     "See license for details.",
  817.     "",
  818.     "",
  819.     0
  820.     };
  821.     static const char *av[] = { "nethack" };
  822.     register const char **pp;
  823.     int i;
  824.     Cardinal num_args;
  825.     Arg args[4];
  826.  
  827.     /* Init windows to nothing. */
  828.     for (i = 0; i < MAX_WINDOWS; i++)
  829.     window_list[i].type = NHW_NONE;
  830.  
  831.     XSetIOErrorHandler((XIOErrorHandler) hangup);
  832.  
  833.     i = 1;
  834.     num_args = 0;
  835.     XtSetArg(args[num_args], XtNallowShellResize, True);    num_args++;
  836.     toplevel = XtAppInitialize(
  837.             &app_context,
  838.             "NetHack",        /* application class */
  839.             (XrmOptionDescList)0, 0,    /* options list */
  840.             &i, av,        /* command line args */
  841.             (String *)0,    /* fallback resources */
  842.             (ArgList)args, num_args);
  843.     XtOverrideTranslations(toplevel,
  844.     XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
  845.  
  846.     /* We don't need to realize the top level widget. */
  847.  
  848. #ifdef TEXTCOLOR
  849.     /* add new color converter to deal with overused colormaps */
  850.     XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
  851.                (XtConvertArgList)nhcolorConvertArgs, 
  852.                XtNumber(nhcolorConvertArgs),
  853.                XtCacheByDisplay, nhFreePixel);
  854. #endif /* TEXTCOLOR */
  855.  
  856.     /* Register the actions mentioned in "actions". */
  857.     XtAppAddActions(app_context, actions, XtNumber(actions));
  858.  
  859.     /* Get application-wide resources */
  860.     XtGetApplicationResources(toplevel, (XtPointer)&appResources,
  861.                   resources, XtNumber(resources),
  862.                   (ArgList)0, ZERO);
  863.  
  864.     /* Initialize other things. */
  865.     init_standard_windows();
  866.     init_extended_commands_popup();
  867.  
  868.     /* Give the window manager an icon to use;  toplevel must be realized. */
  869.     if (appResources.icon && *appResources.icon) {
  870.     struct icon_info *ip;
  871.  
  872.     for (ip = icon_data; ip->name; ip++)
  873.         if (!strcmp(appResources.icon, ip->name)) {
  874.         icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel),
  875.                     XtWindow(toplevel),
  876.                     ip->bits, ip->width, ip->height);
  877.         if (icon_pixmap != None) {
  878.             XWMHints hints;
  879.  
  880.             hints.flags = IconPixmapHint;
  881.             hints.icon_pixmap = icon_pixmap;
  882.             XSetWMHints(XtDisplay(toplevel),
  883.                 XtWindow(toplevel), &hints);
  884.         }
  885.         break;
  886.         }
  887.     }
  888.  
  889.     x_inited = TRUE;    /* X is now initialized */
  890.  
  891.     /* Display the startup banner in the message window. */
  892.     for (pp = banner_text; *pp; pp++)
  893.     X11_putstr(WIN_MESSAGE, 0, *pp);
  894. }
  895.  
  896. /*
  897.  * Let the OS take care of almost everything.  This includes the "main"
  898.  * three windows:  message, map, and status.  Note that if I destroy one of
  899.  * the three main windows, they all will be destroyed, due to their shared
  900.  * parent.  I currently don't check such a thing occuring, so the whole mess
  901.  * will probably crash&burn if I tried it.
  902.  */
  903. /* ARGSUSED */
  904. void X11_exit_nhwindows(dummy)
  905.     const char *dummy;
  906. {
  907.     /* explicitly free the icon pixmap */
  908.     if (icon_pixmap != None) {
  909.     XFreePixmap(XtDisplay(toplevel), icon_pixmap);
  910.     icon_pixmap = None;
  911.     }
  912. }
  913.  
  914.  
  915. /* delay_output ------------------------------------------------------------ */
  916.  
  917. /*
  918.  * Timeout callback for delay_output().  Send a fake message to the map
  919.  * window.
  920.  */
  921. /* ARGSUSED */
  922. static void
  923. d_timeout(client_data, id)
  924.     XtPointer client_data;
  925.     XtIntervalId *id;
  926. {
  927.     XEvent event;
  928.     XClientMessageEvent *mesg;
  929.  
  930.     /* Set up a fake message to the event handler. */
  931.     mesg = (XClientMessageEvent *) &event;
  932.     mesg->type = ClientMessage;
  933.     mesg->message_type = XA_STRING;
  934.     mesg->format = 8;
  935.     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
  936.         XtWindow(window_list[WIN_MAP].w),
  937.         False,
  938.         NoEventMask,
  939.         (XEvent*) mesg);
  940. }
  941.  
  942. /*
  943.  * Delay for 50ms.  This is not implemented asynch.  Maybe later.
  944.  * Start the timeout, then wait in the event loop.  The timeout
  945.  * function will send an event to the map window which will be waiting
  946.  * for a sent event.
  947.  */
  948. void
  949. X11_delay_output()
  950. {
  951.     if (!x_inited) return;
  952.  
  953.     (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
  954.  
  955.     /* The timeout function will enable the event loop exit. */
  956.     (void) x_event(EXIT_ON_SENT_EVENT);
  957. }
  958.  
  959. /* X11_hangup -------------------------------------------------------------- */
  960. /* ARGSUSED */
  961. static void
  962. X11_hangup(w, event, params, num_params)
  963.     Widget w;
  964.     XEvent *event;
  965.     String *params;
  966.     Cardinal *num_params;
  967. {
  968.     (void) hangup();
  969. }
  970.  
  971. /* askname ----------------------------------------------------------------- */
  972. /* ARGSUSED */
  973. static void
  974. askname_delete(w, event, params, num_params)
  975.     Widget w;
  976.     XEvent *event;
  977.     String *params;
  978.     Cardinal *num_params;
  979. {
  980.     nh_XtPopdown(w);
  981.     (void) strcpy(plname, "Mumbles");    /* give them a name... ;-) */
  982.     exit_x_event = TRUE;
  983. }
  984.  
  985. /* Callback for askname dialog widget. */
  986. /* ARGSUSED */
  987. static void
  988. askname_done(w, client_data, call_data)
  989.     Widget w;
  990.     XtPointer client_data;
  991.     XtPointer call_data;
  992. {
  993.     int len;
  994.     char *s;
  995.     Widget dialog = (Widget) client_data;
  996.  
  997.     s = (char *) GetDialogResponse(dialog);
  998.  
  999.     len = strlen(s);
  1000.     if (len == 0) {
  1001.     X11_nhbell();
  1002.     return;
  1003.     }
  1004.  
  1005.     /* Truncate name if necessary */
  1006.     if (len >= sizeof(plname)-1)
  1007.     len = sizeof(plname)-1;
  1008.  
  1009.     (void) strncpy(plname, s, len);
  1010.     plname[len] = '\0';
  1011.  
  1012.     nh_XtPopdown(XtParent(dialog));
  1013.     exit_x_event = TRUE;
  1014. }
  1015.  
  1016. void
  1017. X11_askname()
  1018. {
  1019.     Widget popup, dialog;
  1020.     Arg args[1];
  1021.  
  1022.     XtSetArg(args[0], XtNallowShellResize, True);
  1023.  
  1024.     popup = XtCreatePopupShell("askname", transientShellWidgetClass,
  1025.                    toplevel, args, ONE);
  1026.     XtOverrideTranslations(popup,
  1027.     XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
  1028.  
  1029.     dialog = CreateDialog(popup, "dialog",
  1030.                     askname_done, (XtCallbackProc) 0);
  1031.  
  1032.     SetDialogPrompt(dialog, "What is your name?");    /* set prompt */
  1033.     SetDialogResponse(dialog, "");        /* set default answer */
  1034.  
  1035.     XtRealizeWidget(popup);
  1036.     positionpopup(popup, TRUE);        /* center,bottom */
  1037.  
  1038.     nh_XtPopup(popup, (int)XtGrabExclusive, dialog);
  1039.  
  1040.     /* The callback will enable the event loop exit. */
  1041.     (void) x_event(EXIT_ON_EXIT);
  1042. }
  1043.  
  1044.  
  1045. /* getline ----------------------------------------------------------------- */
  1046. /* This uses Tim Theisen's dialog widget set (from GhostView). */
  1047.  
  1048. static Widget getline_popup, getline_dialog;
  1049.  
  1050. #define CANCEL_STR "\033"
  1051. static char *getline_input;
  1052.  
  1053.  
  1054. /* Callback for getline dialog widget. */
  1055. /* ARGSUSED */
  1056. static void
  1057. done_button(w, client_data, call_data)
  1058.     Widget w;
  1059.     XtPointer client_data;
  1060.     XtPointer call_data;
  1061. {
  1062.     char *s;
  1063.     Widget dialog = (Widget) client_data;
  1064.  
  1065.     s = (char *) GetDialogResponse(dialog);
  1066.     Strcpy(getline_input, s);
  1067.     nh_XtPopdown(XtParent(dialog));
  1068.     exit_x_event = TRUE;
  1069. }
  1070.  
  1071. /* ARGSUSED */
  1072. static void
  1073. getline_delete(w, event, params, num_params)
  1074.     Widget w;
  1075.     XEvent *event;
  1076.     String *params;
  1077.     Cardinal *num_params;
  1078. {
  1079.     Strcpy(getline_input, CANCEL_STR);
  1080.     nh_XtPopdown(w);
  1081.     exit_x_event = TRUE;
  1082. }
  1083.  
  1084. /* Callback for getline dialog widget. */
  1085. /* ARGSUSED */
  1086. static void
  1087. abort_button(w, client_data, call_data)
  1088.     Widget w;
  1089.     XtPointer client_data;
  1090.     XtPointer call_data;
  1091. {
  1092.     Widget dialog = (Widget) client_data;
  1093.  
  1094.     Strcpy(getline_input, CANCEL_STR);
  1095.     nh_XtPopdown(XtParent(dialog));
  1096.     exit_x_event = TRUE;
  1097. }
  1098.  
  1099.  
  1100. void
  1101. X11_getlin(question, input)
  1102.     const char *question;
  1103.     char *input;
  1104. {
  1105.     static boolean need_to_init = True;
  1106.  
  1107.     getline_input = input;
  1108.  
  1109.     flush_screen(1);
  1110.     if (need_to_init) {
  1111.     Arg args[1];
  1112.  
  1113.     need_to_init = False;
  1114.  
  1115.     XtSetArg(args[0], XtNallowShellResize, True);
  1116.  
  1117.     getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass,
  1118.                    toplevel, args, ONE);
  1119.     XtOverrideTranslations(getline_popup,
  1120.         XtParseTranslationTable("<Message>WM_PROTOCOLS: getline_delete()"));
  1121.  
  1122.     getline_dialog = CreateDialog(getline_popup, "dialog",
  1123.                     done_button, abort_button);
  1124.  
  1125.     XtRealizeWidget(getline_popup);
  1126.     XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
  1127.             &wm_delete_window, 1);
  1128.     }
  1129.     SetDialogPrompt(getline_dialog, (String)question);    /* set prompt */
  1130.     SetDialogResponse(getline_dialog, "");    /* set default answer */
  1131.     positionpopup(getline_popup, TRUE);        /* center,bottom */
  1132.  
  1133.     nh_XtPopup(getline_popup, (int)XtGrabNone, getline_dialog);
  1134.  
  1135.     /* The callback will enable the event loop exit. */
  1136.     (void) x_event(EXIT_ON_EXIT);
  1137. }
  1138.  
  1139.  
  1140. /* Display file ------------------------------------------------------------ */
  1141. static const char display_translations[] =
  1142.     "#override\n\
  1143.      <Key>q: dismiss_file()\n\
  1144.      <Key>Escape: dismiss_file()\n\
  1145.      <BtnDown>: dismiss_file()";
  1146.  
  1147.  
  1148. /* WM_DELETE_WINDOW callback for file dismissal. */
  1149. /*ARGSUSED*/
  1150. static void
  1151. delete_file(w, event, params, num_params)
  1152.     Widget w;
  1153.     XEvent *event;
  1154.     String *params;
  1155.     Cardinal *num_params;
  1156. {
  1157.     nh_XtPopdown(w);
  1158.     XtDestroyWidget(w);
  1159. }
  1160.  
  1161. /* Callback for file dismissal. */
  1162. /*ARGSUSED*/
  1163. static void
  1164. dismiss_file(w, event, params, num_params)
  1165.     Widget w;
  1166.     XEvent *event;
  1167.     String *params;
  1168.     Cardinal *num_params;
  1169. {
  1170.     Widget popup = XtParent(w);
  1171.     nh_XtPopdown(popup);
  1172.     XtDestroyWidget(popup);
  1173. }
  1174.  
  1175. void
  1176. X11_display_file(str, complain)
  1177.     const char *str;
  1178.     boolean complain;
  1179. {
  1180.     FILE *fp;
  1181.     Arg args[12];
  1182.     Cardinal num_args;
  1183.     Widget popup, dispfile;
  1184.     Position top_margin, bottom_margin, left_margin, right_margin;
  1185.     XFontStruct *fs;
  1186.     int new_width, new_height;
  1187. #define LLEN 128
  1188.     char line[LLEN];
  1189.     int num_lines;
  1190.  
  1191.     /* Use the port-independent file opener to see if the file exists. */
  1192.     fp = fopen_datafile(str, "r");
  1193.  
  1194.     if (!fp) {
  1195.     if(complain) pline("Cannot open %s.  Sorry.", str);
  1196.  
  1197.     return;    /* it doesn't exist, ignore */
  1198.     }
  1199.  
  1200.     /*
  1201.      * Count the number of lines in the file.  If under the max display
  1202.      * size, use that instead.
  1203.      */
  1204.     num_lines = 0;
  1205.     while (fgets(line, LLEN, fp)) {
  1206.     num_lines++;
  1207.     if (num_lines >= DISPLAY_FILE_SIZE) break;
  1208.     }
  1209.  
  1210.     (void) fclose(fp);
  1211.  
  1212.     /* Ignore empty files */
  1213.     if (num_lines == 0) return;
  1214.  
  1215.     num_args = 0;
  1216.     XtSetArg(args[num_args], XtNtitle, str);    num_args++;
  1217.  
  1218.     popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
  1219.                            toplevel, args, num_args);
  1220.     XtOverrideTranslations(popup,
  1221.     XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
  1222.  
  1223.     num_args = 0;
  1224.     XtSetArg(args[num_args], XtNscrollHorizontal,
  1225.                 XawtextScrollWhenNeeded);    num_args++;
  1226.     XtSetArg(args[num_args], XtNscrollVertical,
  1227.                 XawtextScrollWhenNeeded);    num_args++;
  1228.     XtSetArg(args[num_args], XtNtype, XawAsciiFile);        num_args++;
  1229.     XtSetArg(args[num_args], XtNstring, str);            num_args++;
  1230.     XtSetArg(args[num_args], XtNdisplayCaret, False);        num_args++;
  1231.     XtSetArg(args[num_args], XtNtranslations,
  1232.     XtParseTranslationTable(display_translations));        num_args++;
  1233.  
  1234.     dispfile = XtCreateManagedWidget(
  1235.             "text",            /* name */
  1236.             asciiTextWidgetClass,
  1237.             popup,            /* parent widget */
  1238.             args,            /* set some values */
  1239.             num_args);        /* number of values to set */
  1240.  
  1241.     /* Get font and border information. */
  1242.     num_args = 0;
  1243.     XtSetArg(args[num_args], XtNfont,          &fs);           num_args++;
  1244.     XtSetArg(args[num_args], XtNtopMargin,    &top_margin);    num_args++;
  1245.     XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
  1246.     XtSetArg(args[num_args], XtNleftMargin,   &left_margin);   num_args++;
  1247.     XtSetArg(args[num_args], XtNrightMargin,  &right_margin);  num_args++;
  1248.     XtGetValues(dispfile, args, num_args);
  1249.  
  1250.     /*
  1251.      * Font height is ascent + descent.
  1252.      *
  1253.      * The data files are currently set up assuming an 80 char wide window
  1254.      * and a fixed width font.  Soo..
  1255.      */
  1256.     new_height = num_lines * (fs->ascent + fs->descent) +
  1257.                         top_margin + bottom_margin;
  1258.     new_width  = 80 * fs->max_bounds.width + left_margin + right_margin;
  1259.  
  1260.     /* Set the new width and height. */
  1261.     num_args = 0;
  1262.     XtSetArg(args[num_args], XtNwidth,  new_width);  num_args++;
  1263.     XtSetArg(args[num_args], XtNheight, new_height); num_args++;
  1264.     XtSetValues(dispfile, args, num_args);
  1265.  
  1266.     nh_XtPopup(popup, (int)XtGrabNone, None);
  1267. }
  1268.  
  1269.  
  1270. /* yn_function ------------------------------------------------------------- */
  1271. /* (not threaded) */
  1272.  
  1273. static const char *yn_quitchars = " \n\r";
  1274. static const char *yn_choices;    /* string of acceptable input */
  1275. static char yn_def;
  1276. static char yn_return;        /* return value */
  1277. static char yn_esc_map;        /* ESC maps to this char. */
  1278. static Widget yn_popup;        /* popup for the yn fuction (created once) */
  1279. static Widget yn_label;        /* label for yn function (created once) */
  1280. static boolean yn_getting_num;    /* TRUE if accepting digits */
  1281. static int yn_ndigits;        /* digit count */
  1282. static long yn_val;        /* accumulated value */
  1283.  
  1284. static const char yn_translations[] =
  1285.     "#override\n\
  1286.      <Key>: yn_key()";
  1287.  
  1288. /*
  1289.  * Convert the given key event into a character.  If the key maps to
  1290.  * more than one character only the first is returned.  If there is
  1291.  * no conversion (i.e. just the CTRL key hit) a NULL is returned.
  1292.  */
  1293. char
  1294. key_event_to_char(key)
  1295.     XKeyEvent *key;
  1296. {
  1297.     char keystring[MAX_KEY_STRING];
  1298.     int nbytes;
  1299.     boolean meta = !!(key->state & Mod1Mask);
  1300.  
  1301.     nbytes = XLookupString(key, keystring, MAX_KEY_STRING,
  1302.                (KeySym *)0, (XComposeStatus *)0);
  1303.  
  1304.     /* Modifier keys return a zero lengh string when pressed. */
  1305.     if (nbytes == 0) return '\0';
  1306.  
  1307.     return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
  1308. }
  1309.  
  1310. /*
  1311.  * Called when we get a WM_DELETE_WINDOW event on a yn window.
  1312.  */
  1313. /* ARGSUSED */
  1314. static void
  1315. yn_delete(w, event, params, num_params)
  1316.     Widget w;
  1317.     XEvent *event;
  1318.     String *params;
  1319.     Cardinal *num_params;
  1320. {
  1321.     yn_getting_num = FALSE;
  1322.     /* Only use yn_esc_map if we have choices.  Otherwise, return ESC. */
  1323.     yn_return = yn_choices ? yn_esc_map : '\033';
  1324.     exit_x_event = TRUE;    /* exit our event handler */
  1325. }
  1326.  
  1327. /*
  1328.  * Called when we get a key press event on a yn window.
  1329.  */
  1330. /* ARGSUSED */
  1331. static void
  1332. yn_key(w, event, params, num_params)
  1333.     Widget w;
  1334.     XEvent *event;
  1335.     String *params;
  1336.     Cardinal *num_params;
  1337. {
  1338.     char ch;
  1339.  
  1340.     if(appResources.slow && !input_func)
  1341.     map_input(w, event, params, num_params);
  1342.  
  1343.     ch = key_event_to_char((XKeyEvent *) event);
  1344.  
  1345.     if (ch == '\0') {    /* don't accept nul char or modifier event */
  1346.     /* no bell */
  1347.     return;
  1348.     }
  1349.  
  1350.     if (!yn_choices) {            /* accept any input */
  1351.     yn_return = ch;
  1352.     } else {
  1353.     ch = lowc(ch);            /* move to lower case */
  1354.  
  1355.     if (ch == '\033') {
  1356.         yn_getting_num = FALSE;
  1357.         yn_return = yn_esc_map;
  1358.     } else if (index(yn_quitchars, ch)) {
  1359.         yn_return = yn_def;
  1360.     } else if (index(yn_choices, ch)) {
  1361.         if (ch == '#') {
  1362.         if (yn_getting_num) {    /* don't select again */
  1363.             X11_nhbell();
  1364.             return;
  1365.         }
  1366.         yn_getting_num = TRUE;
  1367.         yn_ndigits = 0;
  1368.         yn_val = 0;
  1369.         return;            /* wait for more input */
  1370.         }
  1371.         yn_return = ch;
  1372.         if (ch != 'y') yn_getting_num = FALSE;
  1373.     } else {
  1374.         if (yn_getting_num) {
  1375.         if (digit(ch)) {
  1376.             yn_ndigits++;
  1377.             yn_val = (yn_val * 10) + (long) (ch - '0');
  1378.             return;            /* wait for more input */
  1379.         }
  1380.         if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) {
  1381.             yn_ndigits--;
  1382.             yn_val = yn_val/ 10;
  1383.             return;            /* wait for more input */
  1384.         }
  1385.         }
  1386.         X11_nhbell();        /* no match */
  1387.         return;
  1388.     }
  1389.  
  1390.     if (yn_getting_num) {
  1391.         yn_return = '#';
  1392.         if (yn_val < 0) yn_val = 0;
  1393.         yn_number = yn_val;    /* assign global */
  1394.     }
  1395.     }
  1396.     exit_x_event = TRUE;    /* exit our event handler */
  1397. }
  1398.  
  1399.  
  1400. char
  1401. X11_yn_function(ques, choices, def)
  1402.     const char *ques;
  1403.     const char *choices;
  1404.     char def;
  1405. {
  1406.     static Boolean need_to_init = True;
  1407.     char buf[QBUFSZ];
  1408.     Arg args[4];
  1409.     Cardinal num_args;
  1410.  
  1411.     yn_choices = choices;    /* set up globals for callback to use */
  1412.     yn_def     = def;
  1413.  
  1414.     /*
  1415.      * This is sort of a kludge.  There are quite a few places in the main
  1416.      * nethack code where a pline containing information is followed by a
  1417.      * call to yn_function().  There is no flush of the message window
  1418.      * (it is implicit in the tty window port), so the line never shows
  1419.      * up for us!  Solution: do our own flush.
  1420.      */
  1421.     if (WIN_MESSAGE != WIN_ERR)
  1422.     display_message_window(&window_list[WIN_MESSAGE]);
  1423.  
  1424.     if (choices) {
  1425.     /* ques [choices] (def) */
  1426.     if ((1 + strlen(ques) + 2 + strlen(choices) + 4) >= QBUFSZ)
  1427.         panic("yn_function:  question too long");
  1428.     if (def)
  1429.         Sprintf(buf, "%s [%s] (%c)", ques, choices, def);
  1430.     else
  1431.         Sprintf(buf, "%s [%s] ", ques, choices);
  1432.  
  1433.     /* escape maps to 'q' or 'n' or default, in that order */
  1434.     yn_esc_map = (index(choices, 'q') ? 'q' :
  1435.              (index(choices, 'n') ? 'n' :
  1436.                         def));
  1437.     } else {
  1438.     if ((1 + strlen(ques)) >= QBUFSZ)
  1439.         panic("yn_function:  question too long");
  1440.     Strcpy(buf, ques);
  1441.     }
  1442.  
  1443.     if (!appResources.slow && need_to_init) {
  1444.     need_to_init = False;
  1445.  
  1446.     XtSetArg(args[0], XtNallowShellResize, True);
  1447.     yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
  1448.                     toplevel, args, ONE);
  1449.     XtOverrideTranslations(yn_popup,
  1450.         XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
  1451.  
  1452.     num_args = 0;
  1453.     XtSetArg(args[num_args], XtNtranslations,
  1454.         XtParseTranslationTable(yn_translations));    num_args++;
  1455.     yn_label = XtCreateManagedWidget("yn_label",
  1456.                 labelWidgetClass,
  1457.                 yn_popup,
  1458.                 args, num_args);
  1459.  
  1460.     XtRealizeWidget(yn_popup);
  1461.     XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
  1462.             &wm_delete_window, 1);
  1463.     }
  1464.  
  1465.     if(appResources.slow)
  1466.     input_func = yn_key;
  1467.  
  1468.     num_args = 0;
  1469.     XtSetArg(args[num_args], XtNlabel, buf);    num_args++;
  1470.     XtSetValues(yn_label, args, num_args);
  1471.  
  1472.     if(!appResources.slow) {
  1473.     /*
  1474.      * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
  1475.      * need to set the label twice to get the size to change.
  1476.      */
  1477.  
  1478.     positionpopup(yn_popup, TRUE);
  1479.     nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label);
  1480.     num_args = 0;
  1481.     XtSetArg(args[num_args], XtNlabel, buf); num_args++; 
  1482.     XtSetValues(yn_label, args, num_args);
  1483.  
  1484.     }
  1485.  
  1486.     yn_getting_num = FALSE;
  1487.     (void) x_event(EXIT_ON_EXIT);
  1488.  
  1489.     if(appResources.slow) {
  1490.     input_func = 0;
  1491.     num_args = 0;
  1492.     XtSetArg(args[num_args], XtNlabel, " ");    num_args++;
  1493.     XtSetValues(yn_label, args, num_args);
  1494.     } else {
  1495.     nh_XtPopdown(yn_popup);    /* this removes the event grab */
  1496.     }
  1497.  
  1498.     return yn_return;
  1499. }
  1500.  
  1501. /* End global functions ==================================================== */
  1502.  
  1503. /*
  1504.  * Before we wait for input via nhgetch() and nh_poskey(), we need to
  1505.  * do some pre-processing.
  1506.  */
  1507. static int
  1508. input_event(exit_condition)
  1509.     int exit_condition;
  1510. {
  1511.     if (WIN_STATUS != WIN_ERR)    /* hilighting on the fancy status window */
  1512.     check_turn_events();
  1513.     if (WIN_MAP != WIN_ERR)    /* make sure cursor is not clipped */
  1514.     check_cursor_visibility(&window_list[WIN_MAP]);
  1515.     if (WIN_MESSAGE != WIN_ERR)    /* reset pause line */
  1516.     set_last_pause(&window_list[WIN_MESSAGE]);
  1517.  
  1518.     return x_event(exit_condition);
  1519. }
  1520.  
  1521.  
  1522. /*ARGSUSED*/
  1523. void
  1524. msgkey(w, data, event)
  1525.     Widget w;
  1526.     XtPointer data;
  1527.     XEvent *event;
  1528. {
  1529.     Cardinal num = 0;
  1530.     map_input(window_list[WIN_MAP].w, event, (String*) 0, &num);
  1531. }
  1532.  
  1533. /*ARGSUSED*/
  1534. static void
  1535. win_visible(w, data, event, flag)    /* only called for autofocus */
  1536.     Widget w;
  1537.     XtPointer data;    /* client_data not used */
  1538.     XEvent *event;
  1539.     Boolean *flag;    /* continue_to_dispatch flag not used */
  1540. {
  1541.     XVisibilityEvent *vis_event = (XVisibilityEvent *)event;
  1542.  
  1543.     if (vis_event->state != VisibilityFullyObscured) {
  1544.     /* one-time operation; cancel ourself */
  1545.     XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
  1546.                  win_visible, (XtPointer) 0);
  1547.     /* grab initial input focus */
  1548.     XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
  1549.     }
  1550. }
  1551.  
  1552. /*
  1553.  * Set up the playing console.  This has three major parts:  the
  1554.  * message window, the map, and the status window.
  1555.  */
  1556. static void
  1557. init_standard_windows()
  1558. {
  1559.     Widget form, message_viewport, map_viewport, status;
  1560.     Arg args[8];
  1561.     Cardinal num_args;
  1562.     Dimension message_vp_width, map_vp_width, status_width, max_width;
  1563.     int map_vp_hd, status_hd;
  1564.     struct xwindow *wp;
  1565.  
  1566.  
  1567.     num_args = 0;
  1568.     XtSetArg(args[num_args], XtNallowShellResize, True);    num_args++;
  1569.     form = XtCreateManagedWidget("nethack",
  1570.                 formWidgetClass,
  1571.                 toplevel, args, num_args);
  1572.  
  1573.     XtAddEventHandler(form, KeyPressMask, False,
  1574.               (XtEventHandler) msgkey, (XtPointer) 0);
  1575.  
  1576.     if (appResources.autofocus)
  1577.     XtAddEventHandler(toplevel, VisibilityChangeMask, False,
  1578.               win_visible, (XtPointer) 0);
  1579.  
  1580.     /*
  1581.      * Create message window.
  1582.      */
  1583.     WIN_MESSAGE = message_win = find_free_window();
  1584.     wp = &window_list[message_win];
  1585.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1586.     wp->popup = (Widget) 0;
  1587.     create_message_window(wp, FALSE, form);
  1588.     message_viewport = XtParent(wp->w);
  1589.  
  1590.  
  1591.     /* Tell the form that contains it that resizes are OK. */
  1592.     num_args = 0;
  1593.     XtSetArg(args[num_args], XtNresizable, True);        num_args++;
  1594.     XtSetArg(args[num_args], XtNleft,       XtChainLeft);    num_args++;
  1595.     XtSetArg(args[num_args], XtNtop,       XtChainTop);        num_args++;
  1596.     XtSetValues(message_viewport, args, num_args);
  1597.  
  1598.     if(appResources.slow) {
  1599.     num_args = 0;
  1600.     XtSetArg(args[num_args], XtNtranslations,
  1601.          XtParseTranslationTable(yn_translations)); num_args++;
  1602.     yn_label = XtCreateManagedWidget("yn_label",
  1603.                      labelWidgetClass,
  1604.                      form,
  1605.                      args, num_args);
  1606.     num_args = 0;
  1607.     XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++;
  1608.     XtSetArg(args[num_args], XtNresizable, True);    num_args++;
  1609.     XtSetArg(args[num_args], XtNlabel, " ");    num_args++;
  1610.     XtSetValues(yn_label, args, num_args);
  1611.     }
  1612.  
  1613.     /*
  1614.      * Create the map window & viewport and chain the viewport beneath the
  1615.      * message_viewport.
  1616.      */
  1617.     map_win = find_free_window();
  1618.     wp = &window_list[map_win];
  1619.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1620.     wp->popup = (Widget) 0;
  1621.     create_map_window(wp, FALSE, form);
  1622.     map_viewport = XtParent(wp->w);
  1623.  
  1624.     /* Chain beneath message_viewport or yn window. */
  1625.     num_args = 0;
  1626.     if(appResources.slow) {
  1627.     XtSetArg(args[num_args], XtNfromVert, yn_label);    num_args++;
  1628.     } else {
  1629.     XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++;
  1630.     }
  1631.     XtSetArg(args[num_args], XtNbottom, XtChainBottom);        num_args++;
  1632.     XtSetValues(map_viewport, args, num_args);
  1633.  
  1634.     /* Create the status window, with the form as it's parent. */
  1635.     status_win = find_free_window();
  1636.     wp = &window_list[status_win];
  1637.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1638.     wp->popup = (Widget) 0;
  1639.     create_status_window(wp, FALSE, form);
  1640.     status = wp->w;
  1641.  
  1642.     /*
  1643.      * Chain the status window beneath the viewport.  Mark the left and right
  1644.      * edges so that they stay a fixed distance from the left edge of the
  1645.      * parent, as well as the top and bottom edges so that they stay a fixed
  1646.      * distance from the bottom of the parent.  We do this so that the status
  1647.      * will never expand or contract.
  1648.      */
  1649.     num_args = 0;
  1650.     XtSetArg(args[num_args], XtNfromVert, map_viewport);    num_args++;
  1651.     XtSetArg(args[num_args], XtNleft,      XtChainLeft);        num_args++;
  1652.     XtSetArg(args[num_args], XtNright,      XtChainLeft);        num_args++;
  1653.     XtSetArg(args[num_args], XtNtop,      XtChainBottom);    num_args++;
  1654.     XtSetArg(args[num_args], XtNbottom,      XtChainBottom);    num_args++;
  1655.     XtSetValues(status, args, num_args);
  1656.  
  1657.  
  1658.     /*
  1659.      * Realize the popup so that the status widget knows it's size.
  1660.      *
  1661.      * If we unset MappedWhenManaged then the DECwindow driver doesn't
  1662.      * attach the nethack toplevel to the highest virtual root window.
  1663.      * So don't do it.
  1664.      */
  1665.     /* XtSetMappedWhenManaged(toplevel, False); */
  1666.     XtRealizeWidget(toplevel);
  1667.     wm_delete_window = XInternAtom(XtDisplay(toplevel),
  1668.                    "WM_DELETE_WINDOW", False);
  1669.     XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
  1670.             &wm_delete_window, 1);
  1671.  
  1672.     /*
  1673.      * Now get the default widths of the windows.
  1674.      */
  1675.     XtSetArg(args[0], XtNwidth, &message_vp_width);
  1676.     XtGetValues(message_viewport, args, ONE);
  1677.     XtSetArg(args[0], XtNwidth, &map_vp_width);
  1678.     XtSetArg(args[1], XtNhorizDistance, &map_vp_hd);
  1679.     XtGetValues(map_viewport, args, TWO);
  1680.     XtSetArg(args[0], XtNwidth, &status_width);
  1681.     XtSetArg(args[1], XtNhorizDistance, &status_hd);
  1682.     XtGetValues(status, args, TWO);
  1683.  
  1684.     /*
  1685.      * Adjust positions and sizes.  The message viewport widens out to the
  1686.      * widest width.  Both the map and status are centered by adjusting
  1687.      * their horizDistance.
  1688.      */
  1689.     if (map_vp_width < status_width || map_vp_width < message_vp_width) {
  1690.     if (status_width > message_vp_width) {
  1691.         XtSetArg(args[0], XtNwidth, status_width);
  1692.         XtSetValues(message_viewport, args, ONE);
  1693.         max_width = status_width;
  1694.     } else {
  1695. /***** The status display looks better when left justified.
  1696.         XtSetArg(args[0], XtNhorizDistance,
  1697.                 status_hd+((message_vp_width-status_width)/2));
  1698.         XtSetValues(status, args, ONE);
  1699. *****/
  1700.         max_width = message_vp_width;
  1701.     }
  1702.     XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2));
  1703.     XtSetValues(map_viewport, args, ONE);
  1704.  
  1705.     } else {    /* map is widest */
  1706.     XtSetArg(args[0], XtNwidth, map_vp_width);
  1707.     XtSetValues(message_viewport, args, ONE);
  1708.  
  1709. /***** The status display looks better when left justified.
  1710.     XtSetArg(args[0], XtNhorizDistance,
  1711.                 status_hd+((map_vp_width-status_width)/2));
  1712.  
  1713.     XtSetValues(status, args, ONE);
  1714. *****/
  1715.     }
  1716.     /*
  1717.      * Clear all data values on the fancy status widget so that the values
  1718.      * used for spacing don't appear.  This needs to be called some time
  1719.      * after the fancy status widget is realized (above, with the game popup),
  1720.      * but before it is popped up.
  1721.      */
  1722.     null_out_status();
  1723.     /*
  1724.      * Set the map size to its standard size.  As with the message window
  1725.      * above, the map window needs to be set to its constrained size until
  1726.      * its parent (the viewport widget) was realized.
  1727.      *
  1728.      * Move the message window's slider to the bottom.
  1729.      */
  1730.     set_map_size(&window_list[map_win], COLNO, ROWNO);
  1731.     set_message_slider(&window_list[message_win]);
  1732.  
  1733.     /* attempt to catch fatal X11 errors before the program quits */
  1734.     (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
  1735.  
  1736.     /* We can now print to the message window. */
  1737.     flags.window_inited = 1;
  1738. }
  1739.  
  1740.  
  1741. void
  1742. nh_XtPopup(w, g, childwid)
  1743.     Widget w;        /* widget */
  1744.     int    g;        /* type of grab */
  1745.     Widget childwid;    /* child to recieve focus (can be None) */
  1746. {
  1747.     XtPopup(w, (XtGrabKind)g);
  1748.     XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
  1749.     if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid);
  1750. }
  1751.  
  1752. void
  1753. nh_XtPopdown(w)
  1754.     Widget w;
  1755. {
  1756.     XtPopdown(w);
  1757.     if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None);
  1758. }
  1759.  
  1760. void
  1761. win_X11_init()
  1762. {
  1763. #ifdef OPENWINBUG
  1764.     /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
  1765.      * two routines will not be found when linking.  An apparently correct
  1766.      * executable is produced, along with nasty messages and a failure code
  1767.      * returned to make.  The routines are in the static libXmu.a and
  1768.      * libXmu.sa.4.0, but not in libXmu.so.4.0.  Rather than fiddle with
  1769.      * static linking, we do this.
  1770.      */
  1771.     if (rn2(2) > 2) {
  1772.     /* i.e., FALSE that an optimizer probably can't find */
  1773.     get_wmShellWidgetClass();
  1774.     get_applicationShellWidgetClass();
  1775.     }
  1776. #endif
  1777.     return;
  1778. }
  1779.  
  1780. /*winX.c*/
  1781.